package file_recently

import (
	"errors"
	"github.com/goccy/go-json"
	"github.com/gogf/gf/errors/gerror"
	"gitlab.local/DO-module/new-filemanage-module/src/constant"
	"gitlab.local/TerraMaster/tos-modules/database"
	"gitlab.local/TerraMaster/tos-modules/database/sqlitemodel"
	"gitlab.local/TerraMaster/tos-modules/tosconfig"
	"gitlab.local/golibrary/user"
	"gitlab.local/golibrary/utils"
	"os"
	"path/filepath"
	"strconv"
	"sync"
	"time"
)

type UploadFileOperationType uint8 //上传文件操作类型

const (
	Rename UploadFileOperationType = iota //重命名
	Cover                                 //覆盖
	Skip                                  //跳过
)

type Recently struct {
	db    database.DbGetter
	mutex sync.Mutex
}

func New(db database.DbGetter) *Recently {
	return &Recently{
		db: db,
	}
}

// RecentlyConf 配置
type RecentlyConf struct {
	FirstWindow         bool                    `json:"first_window"`          //提示展示
	Quantity            int                     `json:"quantity"`              //最近数量
	Security            bool                    `json:"security"`              //安全性
	Coding              string                  `json:"coding"`                //压缩与下载文件名编码(GB18030,GBK,HZGB2312,Big5,UTF8,UTF8BOM,ISO8859_6E,ISO8859_6I,ISO8859_8E,ISO8859_8I,EUCJP,ISO2022JP,ShiftJIS,EUCKR)
	LogIn               bool                    `json:"log_in"`                //是否开启日志记入
	ShareDomain         string                  `json:"share_domain"`          //分享域名及端口
	NoShowHiddenFolder  bool                    `json:"no_show_hidden_folder"` //不显示隐藏文件夹
	UploadFileOperation UploadFileOperationType `json:"upload_file_operation"` //上传文件操作
	ShowThumbnails      bool                    `json:"show_thumbnails"`       //是否显示缩略图
}

type List struct {
	path string
	time int64
}

// WriteRecently 添加最近文件
func (r *Recently) WriteRecently(uname, path string) error {
	if uname == "" {
		return errors.New("uname is empty")
	}
	if !utils.Exists(path) {
		return errors.New("file not exists")
	}
	db := r.db.DbGet()
	r.mutex.Lock()
	defer r.mutex.Unlock()
	var data sqlitemodel.FileRecently
	_ = db.Where("user_name=? and path=?", uname, path).Select("id").Find(&data)
	if data.ID == 0 {
		//新增
		return db.Create(&sqlitemodel.FileRecently{
			UserName:   uname,
			Path:       path,
			CreateTime: time.Now(),
		}).Error
	}
	//更新
	return db.Model(&sqlitemodel.FileRecently{}).Where("id=?", data.ID).Update("create_time", time.Now()).Error
}

// IsRecently 判断用户是否有最近文件
func (r *Recently) IsRecently(uname string) bool {
	logs, err := r.ReadUserLogs(uname)
	if err != nil {
		return false
	}
	return len(logs) > 0
}

// ReadUserLogs 读取某用户最近文件日志
func (r *Recently) ReadUserLogs(uname string) ([]string, error) {
	conf, err := r.GetRecentlyConf(uname)
	if err != nil {
		return make([]string, 0), gerror.Wrap(err, "ReadUserLogs")
	}
	return r.readUserLogs(uname, conf.Quantity)
}

func (r *Recently) readUserLogs(uname string, num int) ([]string, error) {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	data := make([]string, 0)
	db := r.db.DbGet()
	lastId := 0
	limit := 100
	for {
		//清洗数据
		var res []sqlitemodel.FileRecently
		_ = db.Where("user_name=? and id > ?", uname, lastId).Select("id", "path").Order("id asc").Limit(limit).Find(&res)
		if len(res) == 0 { //无数据
			return data, nil
		}
		var wg sync.WaitGroup
		for _, v := range res {
			wg.Add(1)
			go func(v sqlitemodel.FileRecently) {
				if !utils.Exists(v.Path) {
					_ = db.Delete(&sqlitemodel.FileRecently{}, "id=?", v.ID)
				}
				wg.Done()
			}(v)
		}
		wg.Wait()
		if len(res) < limit {
			break
		}
		lastId = res[len(res)-1].ID
	}
	var res []sqlitemodel.FileRecently
	_ = db.Where("user_name=?", uname).Select("id", "path").Order("create_time desc").Limit(num).Find(&res)
	var ids []int
	for _, v := range res {
		ids = append(ids, v.ID)
		data = append(data, v.Path)
	}
	if len(ids) > 0 {
		_ = db.Delete(&sqlitemodel.FileRecently{}, "id not in ?", ids)
	}
	return data, nil
}

// GetRecentlyConf 获取最近配置信息
func (r *Recently) GetRecentlyConf(uname string) (*RecentlyConf, error) {
	src, err := r.recentlySrc(uname)
	if err != nil {
		return nil, gerror.Wrap(err, "GetRecentlyConf")
	}
	file, err := os.ReadFile(src)
	if err != nil {
		return nil, gerror.Wrap(err, "GetRecentlyConf1")
	}
	if len(file) == 0 {
		return &RecentlyConf{
			Quantity:           20,
			Security:           false,
			Coding:             "UTF8",
			LogIn:              false,
			NoShowHiddenFolder: false,
			ShowThumbnails:     true,
		}, nil
	}
	data := &RecentlyConf{}
	if err = json.Unmarshal(file, data); err != nil {
		return nil, gerror.Wrap(err, "GetRecentlyConf2")
	}
	return data, nil
}

// SetRecentlyConf 设置最近配置信息
func (r *Recently) SetRecentlyConf(uname string, cf *RecentlyConf) error { //
	//设置桌面展示状态
	go PromptStateSet(uname, cf.FirstWindow)
	marshal, err := json.Marshal(cf)
	if err != nil {
		return gerror.Wrap(err, "SetRecentlyConf1")
	}
	src, err := r.recentlySrc(uname)
	if err != nil {
		return gerror.Wrap(err, "SetRecentlyConf")
	}
	return os.WriteFile(src, marshal, os.ModePerm)
}

func PromptStateSet(uName string, firstWindow bool) error {
	cfg, err := tosconfig.GetUserConfigDir(uName)
	if err != nil {
		return errors.New("failed to get dir error")
	}
	path := filepath.Join(cfg, constant.PromptStateFile)
	file, err := os.ReadFile(path)
	if err != nil {
		return errors.New("failed to read file error")
	}
	var promptState []string
	if err = json.Unmarshal(file, &promptState); err != nil {
		return errors.New("failed to json parse error")
	}
	if firstWindow {
		if utils.Contains(promptState, "file-manage") == -1 {
			promptState = append(promptState, "file-manage")
		}
	} else {
		valueToRemove := "file-manage"
		for i := 0; i < len(promptState); i++ {
			if promptState[i] == valueToRemove {
				promptState = append(promptState[:i], promptState[i+1:]...)
				break
			}
		}
	}
	byteData, err := json.MarshalIndent(promptState, "", " ")
	if err != nil {
		return errors.New("failed to json parse error")
	}

	saveErr := os.WriteFile(path, byteData, 0644) // 写入配置文件
	if saveErr != nil {
		return errors.New("failed to save data error")
	}

	return nil
}

// recentlySrc 获取用户最近配置path
func (r *Recently) recentlySrc(uname string) (string, error) {
	cfg, err := tosconfig.GetUserConfigDir(uname)
	if err != nil {
		return "", gerror.Wrap(err, "recentlySrc")
	}
	path := filepath.Join(cfg, constant.RecentlyConf)
	if !utils.Exists(path) {
		if e := utils.TouchFile(path); e != nil {
			return "", gerror.Wrap(e, "recentlySrc3")
		}
		lookup, err := user.Lookup(uname)
		if err == nil {
			uid, _ := strconv.Atoi(lookup.Uid)
			gid, _ := strconv.Atoi(lookup.Gid)
			_ = os.Chown(path, uid, gid)
		}
	}
	return path, nil
}

// DelRecently 删除最近
func (r *Recently) DelRecently(path []string) error {
	return r.db.DbGet().Debug().Where("path in ?", path).Delete(&sqlitemodel.FileRecently{}).Error
}
